<template>
  <div class="container">
    <div class="content">
      <div class="left">
        <header class="header">
          <div class="leftHeader">
              <div class="title">
              <label for="">表单生成器</label>
            </div>
            <div class="operation">
              <a-button type="link" @click="handleAddOption">添加选项卡</a-button>
              <a-button type="link" @click="handlePerview">预览</a-button>
              <a-button type="link" @click="handleSubmit">提交</a-button>
              <a-button type="link" @click="handleClearAll">清空</a-button>
            </div>
          </div>
          <div class="leftHeader">
            <label for="">表单名称</label>
            <a-input class="operater" v-model="form.name"></a-input>
            <label for="">表单类型</label>
            <a-select
              class="operater"
              v-model="form.workType"
              :options="dictConfig.workType.options"
            />
            <label for="">业务节点</label>
            <a-select
              class="operater"
              v-model="form.businessFlowId"
              :options="workListOptions"
              @change="getSubWorkList"
            />
            <label for="">业务子节点</label>
            <a-select
              class="operater"
              v-model="form.businessFlowSubId"
              :options="subWorkListOptions"
            />
            <label for="">是否生成菜单项</label>
            <a-switch
              v-model="form.isNeedAddResource"
              checked-children="是"
              un-checked-children="否" />
          </div>
        </header>
        <div class="leftContent">
          <template v-for="(option, idx) in formOptions">
             <cardPanel
              @click.native="handleOptionClick(`${idx}`, option)"
              :key="idx" :option="option"
              :class="currentIndex === `${idx}` ? 'selected' : ''"
              @valueChanged="handleValueChanged"
              @removeOption="handleRemoveOption(idx)"
              @cloneOption="handleCloneOption(option)"
              @addRow="handleAddRow(option)"
              @rowClear="handleRowClear"
            >
              <template v-slot:content>
                <rowItem
                  v-for="(row, i) in option.rows"
                  :key="`${idx}_${i}`" :idx="i"
                  @addItem="handleAddItem(row)"
                  @removeRow="handleRemoveRow(option, i)"
                  @valueChanged="handleValueChanged"
                  :row="row"
                  :option="option"
                  >
                  <template v-slot:rowContent>
                    <optionItem
                      v-for="(item, index) in row.children"
                      :key="`${idx}_${i}_${index}`" :item="item"
                      :selected="currentIndex === `${idx}_${i}_${index}`"
                      :class="currentIndex === `${idx}_${i}_${index}` ? 'selected item' : 'item'"
                      :option="option"
                      @click.native.stop="handleSelect(`${idx}_${i}_${index}`, item)"
                      @cloneItem="handleCloneItem(row, item)"
                      @removeItem="handleRemoveItem(row, index)"
                      :draggable="true"
                      @dragstart.native="dragstart(row, item, index)"
                      @dragenter.native="dragenter($event, row, item, index)"
                      @dragend.native="dragend($event)"
                      @dragover.native="dragover($event)"
                    />
                  </template>
                </rowItem>
              </template>
            </cardPanel>
            <a-divider :key="`${idx}_divider`" v-if="idx !== formOptions.length - 1"></a-divider>
          </template>
        </div>
      </div>
      <div class="right">
        <header class="header rightHeader">控件属性</header>
        <div class="rightContent">
          <optionProperty
            :properties="properties"
            :selectedItem="selectedItem"
            @valueChanged="handleValueChanged"
            @typeChanged="handleTypeChanged"
            @ruleChanged="handleRuleChanged"
            @addChild="handleAddChild"
            @delChild="handleDelChild" />
        </div>
      </div>
    </div>
    <formModal
      ref="formPreview"
      :formOptions="formOptionsFormat"
      :formType="formType"
      :formDeal="{}"
      :modelType="true"
    />
  </div>
</template>

<script>
import _ from 'lodash';
import { mapGetters } from 'vuex';
import dynamicMixin from '@/pages/dynamicForm/components/dynamicMixin';
import { DATA_CODE_SUCCESS, FORMTYPE } from '@/utils/consts';
import config from './components/config';
import cardPanel from './components/card-panel.vue';
import optionItem from './components/option-item.vue';
import rowItem from './components/row-item.vue';
import optionProperty from './components/option-property.vue';
import formModal from './components/form-modal.vue';
import {
  updateFields, addFields, queryParentNode, businessWorkList, businessWorkSub
} from '@/services/formService';

export default {
  mixins: [dynamicMixin],
  name: 'formConfig',
  data() {
    return {
      formOptions: [],
      workListOptions: [],
      subWorkListOptions: [],
      currentIndex: null,
      selectedItem: null,
      properties: [],
      form: {
        businessFlowId: '',
        businessFlowSubId: '',
        name: '',
        workType: '',
        isNeedAddResource: true
      },
      dragStartRef: {},
      dragEndRef: {},
      FORMTYPE,
      formType: FORMTYPE.PREVIEW
    };
  },
  components: {
    cardPanel,
    rowItem,
    optionItem,
    optionProperty,
    formModal
  },
  computed: {
    ...mapGetters('account', ['dictConfig']),
    formOptionsFormat() {
      const cloneOptions = _.cloneDeep(this.formOptions);
      return this.optionFormat(cloneOptions);
    }
  },
  methods: {
    handleAddOption() {
      this.formOptions.push({
        title: '新的选项卡',
        inputName: this.randomCode(6),
        rows: []
      });
    },
    handleRemoveOption(index) {
      this.clearSelectedItem();
      this.formOptions.splice(index, 1);
    },
    handleCloneOption(option) {
      const clone = _.cloneDeep(option);
      clone.inputName = this.randomCode();
      this.formOptions.push(clone);
    },
    handleAddItem(row) {
      const oriRow = row;
      const basicItem = {
        type: 'input', // 控件类型
        label: '输入框', // 表单标题、筛选项标题、列表标题
        inputName: this.randomCode(), // 唯一值用于表单绑定和数据提交
        rules: [], // 验证配置项
        dataParams: [], // 异步请求参数值依赖
        refParams: [], // 联动请求参数值依赖
        relParams: [], // 异步请求的数据影响的值
        isFilter: false, // 是否是筛选项
        isDefault: false, // 是否列展示
        isHide: false, // 表单是否显示
        isDict: false, // 数据是否来自字典表
        isCustom: false // 是否扩展显示
      };
      oriRow.children.push(basicItem);
    },
    handleRemoveRow(option, idx) {
      option.rows.splice(idx, 1);
    },
    handleAddRow(option) {
      const oriOption = option;
      oriOption.rows.push({
        inputName: this.randomCode(7),
        children: []
      });
    },
    handleRowClear(option) {
      const oriOption = option;
      oriOption.rows = [];
    },
    handleCloneItem(option, item) {
      const oriOption = option;
      const clone = _.cloneDeep(item);
      clone.inputName = this.randomCode();
      oriOption.children.push(clone);
    },
    handleRemoveItem(option, index) {
      const oriOption = option;
      if (oriOption.children[index] === this.selectedItem) {
        this.clearSelectedItem();
      }
      oriOption.children.splice(index, 1);
    },
    handleValueChanged(value, prop, option) {
      const oriOption = option;
      this.$set(oriOption, prop, value);
    },
    handleRuleChanged(value, ruleItem) {
      const oriRule = ruleItem;
      Object.keys(value).forEach((key) => {
        this.$set(oriRule, key, value[key]);
      });
    },
    handleTypeChanged(value, prop, option) {
      this.setProperties(value);
      this.handleValueChanged(value, prop, option);
    },
    handleSelect(index, item) {
      if (!(this.currentIndex === index)) {
        this.currentIndex = index;
        this.selectedItem = item;
        this.setProperties(item.type);
      }
    },
    handleOptionClick(index, option) {
      if (!(this.currentIndex === index)) {
        this.currentIndex = index;
        this.selectedItem = option;
        this.setProperties('option');
      }
    },
    handleAddChild(prop, struct) {
      if (!this.selectedItem[prop]) {
        this.$set(this.selectedItem, prop, []);
      }
      this.selectedItem[prop].push(struct);
    },
    handleDelChild(item, prop, idx) {
      const oriItem = item;
      oriItem[prop].splice(idx, 1);
    },
    setProperties(value) {
      this.properties = config[`${value}Config`]
        ? config[`${value}Config`]
        : [];
    },
    handleClearAll() {
      if (this.formOptions.length) {
        this.$confirm({
          title: '警告',
          content: '确定要清空配置吗?',
          okText: '确认',
          cancelText: '取消',
          onOk: () => {
            this.formOptions = [];
            this.clearSelectedItem();
          }
        });
      }
    },
    clearSelectedItem() {
      this.selectedItem = null;
      this.currentIndex = null;
      this.properties = [];
    },
    handlePerview() {
      this.formType = FORMTYPE.PREVIEW;
      this.$refs.formPreview.show();
    },
    async handleSubmit() {
      let msg = '';
      if (!this.form.name) {
        msg = '请配置表单名称';
      }
      if (msg) {
        this.$warning({
          title: '提示',
          content: msg
        });
      } else {
        const data = { ...this.form };
        data.bigInfo = this.formOptions;
        let response = null;
        if (this.form.id) {
          response = await updateFields(data);
        } else {
          response = await addFields(data);
        }
        if (response && response.data.code === DATA_CODE_SUCCESS) {
          this.$router.replace({
            path: '/system/formConfig'
          });
        } else {
          this.$message.error(response.data.message);
        }
      }
    },
    async getFormOptions() {
      const { id } = this.$router.currentRoute.query;
      if (id) {
        const params = { id };
        const response = await queryParentNode(params);
        this.clearSelectedItem();
        if (response.data.code === DATA_CODE_SUCCESS) {
          // eslint-disable-next-line prefer-destructuring
          this.formOptions = _.cloneDeep(response.data.data.bigInfo);
          // eslint-disable-next-line prefer-destructuring
          this.form = response.data.data;
        } else {
          this.formOptions = [];
          this.form = {};
          this.$message.error('表单配置获取失败!');
        }
      } else {
        this.form = {
          businessFlowId: '',
          businessFlowSubId: '',
          name: '',
          workType: '',
          isNeedAddResource: true
        };
        this.formOptions = [];
      }
    },
    async getWorkList() {
      const response = await businessWorkList();
      if (response.data.code === DATA_CODE_SUCCESS) {
        this.workListOptions = this.optionsFormat({
          title: 'name',
          value: 'id',
          list: response.data.data
        });
      }
    },
    async getSubWorkList(id) {
      const response = await businessWorkSub({ id });
      if (response.data.code === DATA_CODE_SUCCESS) {
        this.subWorkListOptions = this.optionsFormat({
          title: 'name',
          value: 'id',
          list: response.data.data
        });
      }
    },
    optionsFormat({ title, value, list }) {
      return list.map((li) => ({ title: li[title], value: li[value] }));
    },
    randomCode(length = 7) {
      const baseStr = 'abcdefghijklmnopqrstuvwxyz';
      const letterArray = [];
      // eslint-disable-next-line no-plusplus
      for (let n = 0; n < length; n++) {
        letterArray.push(baseStr[parseInt(Math.random() * 26, 10)]);
      }
      return letterArray.join('');
    },
    dragstart(row, item, index) {
      this.dragStartRef = { row, item, index };
    },
    dragenter(e, row, item, index) {
      this.dragEndRef = { row, item, index };
      e.preventDefault();
    },
    dragend() {
      if (this.dragStartRef.item !== this.dragEndRef.item) {
        this.dragStartRef.row.children.splice(this.dragStartRef.index, 1);
        this.dragEndRef.row.children.splice(this.dragEndRef.index, 0, this.dragStartRef.item);
      }
    },
    dragover(e) {
      e.preventDefault();
    },
    initial() {
      this.getWorkList();
      this.getFormOptions();
    }
  },
  activated() {
    this.initial();
  }
};
</script>
<style lang="less" scoped>
.container {
  background: #fff;
  height: 50rem;
  overflow: hidden;
  .content {
    display: flex;
    flex-wrap: nowrap;
    height: 100%;
    .header {
      border-bottom: 1px solid lightgray;
      height: 74px;
    }
    .leftHeader {
      display: flex;
      align-items: center;
      padding: 0 1rem;
      .operater {
        margin: 0 .5rem;
        width: 120px;
      }
      .title {
        flex: 0 0 auto;
      }
      .operation {
        flex: 2;
        text-align: right;
      }
    }
    .rightHeader {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .left {
      flex: 1;
      border-right: 1px solid lightgray;
      .leftContent {
        padding: .5rem;
        overflow: auto;
        height: ~"calc(100% - 74px)";
        .selected {
          background: #f6f7ff;
        }
        .item {
          // padding: 1rem 0;
          &:hover {
            cursor: pointer;
          }
        }
      }
    }
    .right {
      flex: 0 0 300px;
      .rightContent {
        overflow: auto;
        height: ~"calc(100% - 74px)";
        padding: .5rem 1rem;
      }
    }
  }
}
</style>
